package com.alogic.cert.xscript;

import com.alogic.cert.CertificateContent;
import com.alogic.cert.bc.KeyPurposeIdTools;
import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsArray;
import com.alogic.xscript.doc.XsObject;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import com.anysoft.util.code.util.ByteUtil;
import org.apache.commons.lang3.StringUtils;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.security.auth.x500.X500Principal;
import java.security.PublicKey;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * 获取当前证书所有信息
 *
 *
 * @since 1.6.13.40 [20210306 duanyy] <br>
 */
public class GetCertAll extends CertificateOperation{
    protected String $tag;
    protected static final String [] sanTypes = new String[]{
            "otherName","rfc822Name","dNSName","x400Address","directoryName","ediPartyName","uniformResourceIdentifier",
            "iPAddress","registeredID"
    };
    public GetCertAll(String tag, Logiclet p) {
        super(tag, p);
    }

    @Override
    public void configure(Properties p){
        super.configure(p);
        $tag = PropertiesConstants.getRaw(p,"tag","");
    }

    @Override
    protected void onExecute(CertificateContent content, XsObject root,
                             XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
        XsObject parent = current;
        String tag = PropertiesConstants.transform(ctx,$tag,"");
        if (StringUtils.isNotEmpty(tag)){
            parent = current.getObjectChild(tag,true);
        }

        X509Certificate cert = content.getCertificate();
        if (cert != null) {
            parent.addProperty("type", cert.getType());
            parent.addProperty("sn", ByteUtil.byte2string(cert.getSerialNumber().toByteArray(), true, true));
            parent.addProperty("version", cert.getVersion());
            parent.addProperty("signAlgorithm", cert.getSigAlgName());
            parent.addProperty("signAlgorithmOID", cert.getSigAlgOID());
            parent.addProperty("notBefore", cert.getNotBefore().getTime());
            parent.addProperty("notAfter", cert.getNotAfter().getTime());
            parent.addProperty("ca",cert.getBasicConstraints()>=0);

            byte[] algPara = cert.getSigAlgParams();
            if (algPara != null) {
                parent.addProperty("signAlgorithmPara", ByteUtil.byte2string(algPara, true, true));
            }

            byte[] signature = cert.getSignature();
            if (signature != null) {
                parent.addProperty("signature", ByteUtil.byte2string(signature, true, true));
            }

            boolean[] baseUsage = cert.getKeyUsage();
            if (baseUsage != null && baseUsage.length >= 9) {
                XsObject usage = current.getObjectChild("usage", true);
                usage.addProperty("digitalSignature", baseUsage[0]);
                usage.addProperty("nonRepudiation", baseUsage[1]);
                usage.addProperty("keyEncipherment", baseUsage[2]);
                usage.addProperty("dataEncipherment", baseUsage[3]);
                usage.addProperty("keyAgreement", baseUsage[4]);
                usage.addProperty("keyCertSign", baseUsage[5]);
                usage.addProperty("cRLSign", baseUsage[6]);
                usage.addProperty("encipherOnly", baseUsage[7]);
                usage.addProperty("decipherOnly", baseUsage[8]);
            }

            try {
                List<String> extendedUsage = cert.getExtendedKeyUsage();
                XsObject usage = current.getObjectChild("usage-ext", true);
                for (String item : extendedUsage) {
                    String kpName = KeyPurposeIdTools.getName(item);
                    if (StringUtils.isNotEmpty(kpName)) {
                        usage.addProperty(kpName, true);
                    }
                }
            } catch (Exception ex) {

            }

            X500Principal issuer = cert.getIssuerX500Principal();
            if (issuer != null) {
                XsObject issuerObject = parent.getObjectChild("issuer", true);
                String name = issuer.getName(X500Principal.RFC2253);
                issuerObject.addProperty("raw", name);
                try {
                    LdapName ldapName = new LdapName(name);
                    List<Rdn> list = ldapName.getRdns();
                    for (Rdn rdn : list) {
                        issuerObject.addProperty(rdn.getType(), rdn.getValue().toString());
                    }

                    Collection<List<?>> subjectAltNames = cert.getIssuerAlternativeNames();
                    if (subjectAltNames != null) {
                        XsArray array = issuerObject.getArrayChild("sans", true);
                        Iterator<List<?>> iter = subjectAltNames.iterator();
                        while (iter.hasNext()) {
                            List<?> l = iter.next();
                            if (l.size() != 2) {
                                continue;
                            }
                            XsObject san = array.newObject();
                            san.addProperty("type", getSanTypeName(l.get(0).toString()));
                            san.addProperty("value", l.get(1).toString());
                            array.add(san);
                        }
                    }
                } catch (InvalidNameException e) {
                    log("issuer name of x509 error.", "error");
                } catch (CertificateParsingException e) {
                    log("subject alt names of x509 error.", "error");
                }
            }

            X500Principal self = cert.getSubjectX500Principal();
            if (self != null) {
                XsObject subjectObject = parent.getObjectChild("subject", true);
                String name = self.getName(X500Principal.RFC2253);
                subjectObject.addProperty("raw", name);
                try {
                    LdapName ldapName = new LdapName(name);
                    List<Rdn> list = ldapName.getRdns();
                    for (Rdn rdn : list) {
                        subjectObject.addProperty(rdn.getType(), rdn.getValue().toString());
                    }

                    Collection<List<?>> subjectAltNames = cert.getSubjectAlternativeNames();
                    if (subjectAltNames != null) {
                        XsArray array = subjectObject.getArrayChild("sans", true);
                        Iterator<List<?>> iter = subjectAltNames.iterator();
                        while (iter.hasNext()) {
                            List<?> l = iter.next();
                            if (l.size() != 2) {
                                continue;
                            }
                            XsObject san = array.newObject();
                            san.addProperty("type", getSanTypeName(l.get(0).toString()));
                            san.addProperty("value", l.get(1).toString());
                            array.add(san);
                        }
                    }
                } catch (InvalidNameException e) {
                    log("subject name of x509 error.", "error");
                } catch (CertificateParsingException e) {
                    log("subject alt names of x509 error.", "error");
                }
            }


            PublicKey publicKey = cert.getPublicKey();
            if (publicKey != null) {
                XsObject pkObject = parent.getObjectChild("public", true);
                pkObject.addProperty("algorithm", publicKey.getAlgorithm());
                pkObject.addProperty("format", publicKey.getFormat());
                pkObject.addProperty("content", ByteUtil.byte2string(publicKey.getEncoded(), true, true));
            }
        }
    }

    public static String getSanTypeName(String type){
        int index = 0;
        try{
            index = Integer.parseInt(type);
        }catch (NumberFormatException ex){

        }
        if (index <0 || index >= sanTypes.length){
            index = 0;
        }
        return sanTypes[index];
    }
}